package com.bifrost.service.impl;

import com.alibaba.fastjson.JSON;
import com.alicp.jetcache.anno.CacheType;
import com.alicp.jetcache.anno.Cached;
import com.bifrost.common.model.ResultModel;
import com.bifrost.common.util.HttpRequestUtils;
import com.bifrost.constant.InventoryType;
import com.bifrost.constant.ProductSkuType;
import com.bifrost.constant.ProductSortType;
import com.bifrost.entity.*;
import com.bifrost.repository.*;
import com.bifrost.service.*;
import com.bifrost.service.ProductService;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

import javax.persistence.criteria.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

/**
 * @author rangjf
 * @date 2017/10/31
 */
@Service("mallService")
public class MallServiceImpl implements MallService {

    @Autowired
    CategoryRepository categoryRepository;
    @Autowired
    ProductRepository productRepository;
    @Autowired
    ProductSkuRepository productSkuRepository;
    @Autowired
    ProductSkuService productSkuService;
    @Autowired
    ProductReviewRepository productReviewRepository;
    @Autowired
    CustomerRepository customerRepository;
    @Autowired
    SkuMediaMapRepository skuMediaMapRepository;
    @Autowired
    ProductBrandRepository productBrandRepository;
    @Autowired
    SkuAssociationRepository skuAssociationRepository;
    @Autowired
    InventoryService inventoryService;
    @Autowired
    SkuMediaMapService skuMediaMapService;
    @Autowired
    StoreService storeService;
    @Autowired
    ProductService productService;
    @Autowired
    CategoryService categoryService;
    @Autowired
    ProductReviewRepository reviewRepository;
    @Autowired
    RedisTemplate<String,String> redisTemplate;
    @Autowired
    ProductSkuService skuService;

    private static final Logger logger = LoggerFactory.getLogger(MallServiceImpl.class);

    /**
     * 获取商品类别
     *
     * @param store store
     * @param parentId parentId
     * @return
     */
    @Override
    public List<Category> getCategories(MerchantStore store, Long parentId) {
        //查看第二级目录
        if(parentId != null){
            List<Category> categoryList = categoryRepository.getByParentId(parentId);
            if(CollectionUtils.isEmpty(categoryList)){
                return null;
            }
            return categoryList.stream().sorted(Comparator.comparing(Category::getSortOrder)).collect(Collectors.toList());
        }
        //倒数第二级
        List<Category> parentCategoryList = new ArrayList<>();
        List<BigInteger> finalCategoryIdList = productSkuRepository.getDepthCategoryIdByMerchantStore(store.getMerchantId());
        for(BigInteger id : finalCategoryIdList){
            if(id == null){
                continue;
            }
            Category currentCategory = categoryRepository.findOne(id.longValue());
            Category parentCategory = categoryRepository.findOne(currentCategory.getParentId());
            if(parentCategory != null){
                parentCategoryList.add(parentCategory);
            }
        }
        return  parentCategoryList.stream().distinct().sorted(Comparator.comparing(Category::getSortOrder)).collect(Collectors.toList());
    }
    @Override
    public List<Map> getCategories(Long storeId, Long parentId){
        Assert.notNull(storeId,"门店不能为空");
        String key = String.format("%s:%s",storeId,parentId==null?"all":parentId);
        String value = redisTemplate.boundValueOps(key).get();
        if(StringUtils.isBlank(value)){
            List<Map> categories = new ArrayList<>();
            synchronized (key.intern()){
                value = redisTemplate.boundValueOps(key).get();
                if(StringUtils.isBlank(value)){
                    MerchantStore store = storeService.getMerchantStore(storeId);
                    List<Category> categoryList = getCategories(store, parentId);
                    if (CollectionUtils.isEmpty(categoryList)) {
                        return null;
                    }

                    categoryList.forEach(category -> {
                        Map map = new HashMap(3);
                        map.put("categoryId", category.getCategoryId());
                        Media media = category.getMedia();
                        map.put("imgUrl", media == null ? "" : HttpRequestUtils.getUrl(media.getUrl()));
                        map.put("categoryName", category.getCategoryName());
                        Boolean isVisiable = category.getIsVisible();
                        if (isVisiable != null && isVisiable) {
                            categories.add(map);
                        }
                    });
                    if(categories!=null)
                    {
                        redisTemplate.boundValueOps(key).set(JSON.toJSONString(categories),10, TimeUnit.MINUTES);
                    }
                    return categories;
                }
            }
        }
        return JSON.parseObject(value,List.class);


    }
    /**
     * 按类别获取商品默认sku
     *
     * @param category 类目Id
     * @param page     当前页数
     * @param pageSize 类目Id
     * @param sortType 排序方式
     * @param minPrice 最低价
     * @param maxPrice 最高价
     * @param querykey 搜索关键字
     * @param brandIds 品牌Id字符串数组
     * @return List
     */
    @Override
    public Page<ProductSku> getProducts(Category category, Integer page, Integer pageSize, String sortType, String minPrice, String maxPrice, String querykey, String brandIds) {
       return  getProductsByStore(null, category, page, pageSize, sortType, minPrice, maxPrice, querykey, brandIds);
    }

    /**
     * 获取门店指定类别下的商品（默认sku）列表
     *
     * @param store    门店
     * @param category 类目Id
     * @param page     当前页数
     * @param pageSize 类目Id
     * @param sortType 排序方式
     * @param minPrice 最低价
     * @param maxPrice 最高价
     * @param querykey 搜索关键字
     * @param brandIds 品牌Id字符串数组
     * @return List
     */
    @Override
    public Page<ProductSku> getProductsByStore(MerchantStore store, Category category, Integer page, Integer pageSize, String sortType, String minPrice, String maxPrice, String querykey, String brandIds) {
        Pageable pageable = null;
        //商品排序 0:综合排序，1：价格升序，2：价格降序，3：销量降序
        if (ProductSortType.MULTIPLE.equals(sortType)) {
            List<Sort.Order> orders = new ArrayList<>();
            orders.add(new Sort.Order(Sort.Direction.ASC, "sortOrder"));
            pageable = new PageRequest(page - 1, pageSize, new Sort(orders));
        } else if (ProductSortType.PEICEUP.equals(sortType)) {
            pageable = new PageRequest(page - 1, pageSize, new Sort(Sort.Direction.ASC, "salePrice"));
        } else if (ProductSortType.PRICEDOWN.equals(sortType)) {
            pageable = new PageRequest(page - 1, pageSize, new Sort(Sort.Direction.DESC, "salePrice"));
        } else if (ProductSortType.SALECOUNT.equals(sortType)) {
            pageable = new PageRequest(page - 1, pageSize, new Sort(Sort.Direction.DESC, "saleCount"));
        } else {
            pageable = new PageRequest(page - 1, pageSize);
        }
        Specification<ProductSku> query = new Specification<ProductSku>() {
            @Override
            public Predicate toPredicate(Root<ProductSku> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
                List<Predicate> predicates = new ArrayList<>();
                if (store!=null) {
                    predicates.add(cb.equal(root.get("merchantId"),store.getMerchantId()));
                }
                //Product 可用
                Join<ProductSku, Product> product = root.join("product", JoinType.INNER);
                Path<Boolean> archived = product.get("archived");
                predicates.add(cb.or(cb.isFalse(archived),cb.isNull(archived)));
                predicates.add(cb.isTrue(product.get("available")));
                predicates.add(cb.isTrue(root.get("isDefault")));
                /*if (StringUtils.isEmpty(querykey)) { //如果是按照名称来查找 以sku形式展示
                    predicates.add(cb.isTrue(root.get("isDefault")));
                }else {
                    predicates.add(cb.isFalse(root.get("isDefault")));
                }*/
                //predicates.add(cb.isTrue(root.get("merchantability")));
                predicates.add(cb.notEqual(root.get("itemType"), ProductSkuType.GIFTWRAP));
                if (category != null) {
                    if(Boolean.TRUE.equals(category.isParent())){
                        List<Category> bottomList = getBottomCategory(category);
                        CriteriaBuilder.In<Category> in = cb.in(root.get("category"));
                        for (Category c : bottomList) {
                            in.value(c);
                        }
                        predicates.add(in);
                    }
                    if(Boolean.FALSE.equals(category.isParent())){
                        predicates.add(cb.equal(root.get("category"), category));
                    }
                }
                if (!StringUtils.isEmpty(minPrice) && StringUtils.isEmpty(maxPrice)) {
                    predicates.add(cb.lessThanOrEqualTo(root.get("salePrice"), new BigDecimal(minPrice)));
                }
                if (!StringUtils.isEmpty(maxPrice) && StringUtils.isEmpty(minPrice)) {
                    predicates.add(cb.greaterThanOrEqualTo(root.get("salePrice"), new BigDecimal(maxPrice)));
                }
                if (!StringUtils.isEmpty(minPrice) && !StringUtils.isEmpty(maxPrice) && minPrice.compareTo(maxPrice) < 0) {
                    predicates.add(cb.between(root.get("salePrice"), new BigDecimal(minPrice), new BigDecimal(maxPrice)));
                }
                if (!StringUtils.isEmpty(querykey)) {
                    String key ="%" + querykey + "%";
                    predicates.add(cb.or(cb.like(root.get("keyword"),key),cb.like(root.get("skuName"), key)));
                }
                if (!StringUtils.isEmpty(brandIds)) {
                    String[] brands = brandIds.split(",");
                    CriteriaBuilder.In<Long> in = cb.in(root.get("productBrandId"));
                    for (String brandId : brands) {
                        in.value(Long.parseLong(brandId));
                    }
                    predicates.add(in);
                }
                Predicate[] p = new Predicate[predicates.size()];
                return cb.and(predicates.toArray(p));
            }
        };
        return productSkuRepository.findAll(query, pageable);
    }

    private List<Category> getBottomCategory(Category category){
        List<Category> categoryList = new ArrayList<>();
        if(Boolean.TRUE.equals(category.isParent())){
            List<Category> childrenList = categoryService.findByParentId(category.getCategoryId());
            for (Category c : childrenList) {
                categoryList.addAll(getBottomCategory(c));
            }
        }else {
            categoryList.add(category);
        }
       return categoryList;
    }

    /**
     * 获取商品信息
     *
     * @param skuId skuId
     * @return List
     */
    @Override
    public Map getProductDetail(Long skuId) {
        Map detailMaps = new HashMap(2);
        ProductSku sku = productSkuService.getSkuById(skuId);
        if (sku != null) {
            productSkuService.pricingBundleProduct(sku);
            Product product = sku.getProduct();
            MerchantStore store = storeService.getMerchantStore(product.getMerchantId());
            Map productSkuMap = new HashMap(17);
            productSkuMap.put("storeId", store.getMerchantId());
            productSkuMap.put("storeName", store.getStoreName());
            productSkuMap.put("price", sku.getSalePrice());
            productSkuMap.put("guidePrice", sku.getRetailPrice());
            productSkuMap.put("skuName", sku.getSkuName());
            productSkuMap.put("skuDescription", sku.getSkuDescription());
            productSkuMap.put("skuId", sku.getSkuId());
            productSkuMap.put("skuAssociation", productSkuService.getSkuGiftAndService(sku));
            Boolean skuIsAvailable = sku.getAvailable();
            if(skuIsAvailable == null || Boolean.FALSE.equals(skuIsAvailable)){
                productSkuMap.put("isAvalible", false);
                productSkuMap.put("unAvalibleReson", "商品已下架");
            }else {
                boolean inventory = inventoryService.checkInventory(sku);
                productSkuMap.put("isAvalible", inventory);
                if (!inventory) {
                    productSkuMap.put("stock", 0);
                    productSkuMap.put("unAvalibleReson", "库存不足");
                }else{
                    if(InventoryType.ALWAYS_AVAILABLE.equalsIgnoreCase(sku.getInventoryType())){
                        productSkuMap.put("stock", 999999);
                    }else{
                        if(!Boolean.TRUE.equals(sku.isDefault())){
                            productSkuMap.put("stock", sku.getQuantityAvailable());
                        }else{
                            Integer available = inventoryService.retrieveQuantityAvailable(product);
                            productSkuMap.put("stock", available==null?0:available);
                        }
                    }
                }
            }
            productSkuMap.put("images", null);
            List<Media> medias = skuMediaMapService.getPrimaryMedia(sku);
            if (CollectionUtils.isEmpty(medias)&&!Boolean.TRUE.equals(sku.isDefault())) {
                ProductSku defaultSku = product.getDefaultSku();
                medias = skuMediaMapService.getPrimaryMedia(defaultSku);
            }
            if (CollectionUtils.isNotEmpty(medias)) {
                List images = medias.stream().map(Media::getUrl).collect(Collectors.toList());
                productSkuMap.put("images", images);
            }
            //获取已审核评论数
            Integer reviewCount = reviewRepository.countFinalSkuReview(sku);
            productSkuMap.put("reviewCount", reviewCount==null?0:reviewCount);
            productSkuMap.put("longDescription", sku.getLongDescription());
            productSkuMap.put("canAddCart", !Boolean.FALSE.equals(sku.getCanAddCart()));
            detailMaps.put("product", productSkuMap);
            detailMaps.put("shippingType", sku.getFulfillmentType());
            detailMaps.put("skus", productSkuService.getAllSku(product));
            detailMaps.put("productOptions", productService.getAllProductOptionValues(product));
            return detailMaps;
        }
        return detailMaps;
    }

    /**
     * 获取商品详细信息-有赞结构
     *
     * @param skuId
     * @return
     */
    @Override
    public Map getDetail(Long skuId) {
        Map detailMaps = new HashMap(2);
        ProductSku sku = productSkuService.getSkuById(skuId);
        if (sku != null) {
            productSkuService.pricingBundleProduct(sku);
            Product product = sku.getProduct();
            ProductSku defaultSku = product.getDefaultSku();
            MerchantStore store = storeService.getMerchantStore(product.getMerchantId());
            Map productSkuMap = new HashMap(17);
            productSkuMap.put("storeId", store.getMerchantId());
            productSkuMap.put("storeName", store.getStoreName());
            productSkuMap.put("price", sku.getSalePrice());
            productSkuMap.put("guidePrice", sku.getRetailPrice());
            productSkuMap.put("skuName", sku.getSkuName());
            productSkuMap.put("skuDescription", sku.getSkuDescription());
            productSkuMap.put("skuId", sku.getSkuId());
            productSkuMap.put("skuAssociation", productSkuService.getSkuGiftAndService(sku));
            productSkuMap.put("isAvalible", true);
            Boolean skuIsAvailable = sku.getAvailable();
            if(skuIsAvailable == null || Boolean.FALSE.equals(skuIsAvailable)){
                productSkuMap.put("isAvalible", false);
                productSkuMap.put("unAvalibleReson", "商品已下架");
            }else {
                Integer inventory = inventoryService.retrieveQuantityAvailable(product);
                if(inventory == null || inventory <= 0){
                    productSkuMap.put("isAvalible", false);
                    productSkuMap.put("stock", 0);
                    productSkuMap.put("unAvalibleReson", "库存不足");
                }else{
                    if(InventoryType.ALWAYS_AVAILABLE.equalsIgnoreCase(sku.getInventoryType())){
                        productSkuMap.put("stock", 999999);
                        productSkuMap.put("hideStock",false);
                    }else{
                        productSkuMap.put("hideStock",false);
                        if(!Boolean.TRUE.equals(sku.isDefault())){
                            productSkuMap.put("stock", sku.getQuantityAvailable());
                        }else{
                            Integer available = inventoryService.retrieveQuantityAvailable(product);
                            productSkuMap.put("stock", available==null?0:available);
                        }
                    }
                }
            }
            productSkuMap.put("images", null);
            List<Media> medias = skuMediaMapService.getPrimaryMedia(sku);
            if (CollectionUtils.isEmpty(medias)&&!Boolean.TRUE.equals(sku.isDefault())) {
                medias = skuMediaMapService.getPrimaryMedia(defaultSku);
            }
            if (CollectionUtils.isNotEmpty(medias)) {
                List images = medias.stream().filter(media -> {
                    return media!=null;
                }).map(media -> {
                    return HttpRequestUtils.getUrl(media.getUrl());
                }).filter(url->{
                   return  StringUtils.isNotEmpty(url);
                }).collect(Collectors.toList());
                productSkuMap.put("images", images);
            }
            //获取已审核评论数
            Integer reviewCount = reviewRepository.countFinalSkuReview(sku);
            productSkuMap.put("reviewCount", reviewCount==null?0:reviewCount);
            productSkuMap.put("longDescription", sku.getLongDescription());
            productSkuMap.put("canAddCart", !Boolean.FALSE.equals(sku.getCanAddCart()));
            productSkuMap.put("noneSku",Boolean.TRUE.equals(defaultSku.getMerchantability()));
            detailMaps.put("product", productSkuMap);
            detailMaps.put("shippingType", sku.getFulfillmentType());
            Map skuMap = productSkuService.getSkusMap(product);
            List<Map> skus = skuMap==null?null:(List<Map>)skuMap.get("skus");
            if (!sku.isDefault()){
                detailMaps.put("initialSku",skus.stream().filter(map -> {
                    return sku.getSkuId().equals((Long) map.get("id"));
                }).findFirst().orElseGet(null));
            }
            detailMaps.put("skus", skus);
            detailMaps.put("productOptions", skuMap==null?null:skuMap.get("options"));
            return detailMaps;
        }
        return detailMaps;
    }

    /**
     * 获取商品图文描述信息/规格参数
     *
     * @param skuId skuId
     * @return List
     */
    @Override
    @Cached(name = "Product:productDesc:" , cacheType = CacheType.REMOTE,localLimit = 50 ,expire =3600 )
    public Map getProductDesc(Long skuId) {
        ProductSku productSku = productSkuService.getSkuById(skuId);
        Map<String, Object> map = new HashMap<>(2);
        List<String> descriptions = new ArrayList<String>();
        List<Media> descMedias = skuMediaMapService.getDescriptionMedia(productSku);
        if (CollectionUtils.isEmpty(descMedias)) {
            ProductSku defaultSKu = productSku.getProduct().getDefaultSku();
            descMedias = skuMediaMapService.getDescriptionMedia(defaultSKu);
        }
        if (CollectionUtils.isNotEmpty(descMedias)) {
            descMedias.forEach(media -> {
                descriptions.add(HttpRequestUtils.getUrl(media.getUrl()));
            });
        }
        map.put("descriptions", descriptions);
        Map paramMap = new HashMap(5);
        paramMap.put("greade", productSku.getGrade());
        paramMap.put("model", productSku.getModel());
        paramMap.put("place", productSku.getOriginPlace());
        paramMap.put("skuCode", productSku.getSkuCode());
        paramMap.put("unit", productSku.getUnit());
        map.put("param", paramMap);
        return map;
    }

    @Override
    @Cached(name = "Product:getHotProducts:" ,  expire = 10000 ,
            key = "#store.merchantId + ':'+ #queryKey + ':' + #brandIds + ':' + #page + ':' + #pageSize ")
    public ResultModel getHotProducts(MerchantStore store, String queryKey, String brandIds, Integer page, Integer pageSize) {
        Pageable pageable = new PageRequest(page - 1, pageSize, Sort.Direction.ASC,"sortOrder");
        Specification<Product> specification = new Specification<Product>() {
            @Override
            public Predicate toPredicate(Root<Product> root, CriteriaQuery<?> cq, CriteriaBuilder cb) {
                List<Predicate> predicates = new ArrayList<>();
                predicates.add(cb.equal(root.get("archived"), Boolean.FALSE));
                predicates.add(cb.equal(root.get("available"), Boolean.TRUE));
                predicates.add(cb.equal(root.get("merchantId"), store.getMerchantId()));
                predicates.add(cb.equal(root.get("featuredProduct"), Boolean.TRUE));
                if (StringUtils.isNotEmpty(queryKey)) {
                    predicates.add(cb.like(root.get("productName"), "%" + queryKey + "%"));
                }
                if (!StringUtils.isEmpty(brandIds)) {
                    String[] brands = brandIds.split(",");
                    CriteriaBuilder.In<ProductBrand> in = cb.in(root.get("productBrand"));
                    for (String brandId : brands) {
                        ProductBrand pb = new ProductBrand();
                        pb.setProductBrandId(Long.parseLong(brandId));
                        in.value(pb);
                    }
                    predicates.add(in);
                }
                Predicate[] p = new Predicate[predicates.size()];
                return cb.and(predicates.toArray(p));
            }
        };
        return getResultModel(productRepository.findAll(specification, pageable));



    }

    private ResultModel getResultModel(Page<Product> data) {
        if(data != null && data.hasContent()){
            List<ProductSku> skuList = data.getContent().stream().map(Product::getDefaultSku).collect(Collectors.toList());
            if (CollectionUtils.isNotEmpty(skuList)) {
                List<Map> skus = new ArrayList<>();
                skuList.forEach(productSku -> {
                    //skuService.pricingBundleProduct(productSku);
                    Map map = new HashMap(14);
                    map.put("skuId", productSku.getSkuId());
                    map.put("skuName", productSku.getSkuName());
                    map.put("salePrices", productSku.getSalePrice());
                    map.put("retailPrices", productSku.getRetailPrice());
                    map.put("salesCount", productSku.getSaleCount());
                    Integer reviewCount = reviewRepository.countFinalSkuReview(productSku);
                    map.put("reviewCount", reviewCount==null?0:reviewCount);
                    map.put("skuDesc", productSku.getSkuDescription());
                    Media media = skuService.getSkuDefaultMedia(productSku);
                    map.put("url", media == null ? null : HttpRequestUtils.getUrl(media.getUrl()));
                    Integer inventory = inventoryService.retrieveQuantityAvailable(productSku.getProduct());
                    map.put("isAvalible", true);
                    if (inventory==null||inventory<=0) {
                        map.put("isAvalible", false);
                        map.put("unAvalibleReson", "库存不足");
                    }
                    map.put("canAddCart", !Boolean.FALSE.equals(productSku.getCanAddCart()));
                    map.put("shippingType", productSku.getFulfillmentType());
                    map.put("skuAssociation", skuService.getSkuGiftAndService(productSku));
                    skus.add(map);
                });
                return ResultModel.ok()
                        .put("total", data.getTotalElements())
                        .put("page", data.getNumber())
                        .put("pageSize", data.getSize())
                        .put("totalPages", data.getTotalPages())
                        .put("skus", skus);
            }
        }
        return ResultModel.ok()
                .put("total", 0)
                .put("page", 0)
                .put("pageSize", 0)
                .put("totalPages", 0)
                .put("skus", null);
    }
}
